package cn.stard.basic.service.impl;

import cn.stard.basic.constants.BaseConstants;
import cn.stard.basic.dto.Longin;
import cn.stard.basic.dto.Wecheatdto;
import cn.stard.basic.exception.BusinessException;
import cn.stard.basic.jwt.JwtUtils;
import cn.stard.basic.jwt.RsaUtils;
import cn.stard.basic.jwt.UserInfo;
import cn.stard.basic.service.ILonginservice;
import cn.stard.basic.util.AjaxResult;
import cn.stard.basic.util.HttpUtil;
import cn.stard.basic.util.MD5Utils;
import cn.stard.basic.util.StrUtils;
import cn.stard.system.domain.Menu;
import cn.stard.system.mapper.MenuMapper;
import cn.stard.system.mapper.PermissionMapper;
import cn.stard.user.domain.Logininfo;
import cn.stard.user.domain.User;
import cn.stard.user.domain.Wxuser;
import cn.stard.user.mapper.LogininfoMapper;
import cn.stard.user.mapper.UserMapper;
import cn.stard.user.mapper.WxuserMapper;
import com.alibaba.fastjson.JSONObject;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.security.PrivateKey;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@Service
public class LonginserviceImpl implements ILonginservice {
@Autowired
private LogininfoMapper logininfoMapper;
    @Autowired
    private UserMapper userMapper;
@Autowired
private WxuserMapper wxuserMapper;
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private PermissionMapper permissionMapper;
@Autowired
private MenuMapper menuMapper;

    @Value("${jwt.rsa.pri}")
    private String jwtpri;
// 在yml中映射使用注解注入依赖 使用spel表达式获取私钥路径

//    /*无状态token方案*/
//    @Override
//    public HashMap<String, Object> logininfo(Longin longin) {
//        /*先获取从前端拿到的每个参数数据*/
//        String password = longin.getPassword();
//        String username = longin.getUsername();
//        String type = longin.getType();
//        /*给参数做非空判断*/
//        if (StringUtils.isEmpty(password)
//                ||StringUtils.isEmpty(username)
//                ||StringUtils.isEmpty(type)){
//            throw new BusinessException("参数不能为空,请重新输入");
//        }
//        /*根据账户和type查询登录用户是否存在不存在抛出异常*/
//        /*因为是登录所以要查询登录里面用户是否存在,包含三个参数的是已经封装的对象Longin*/
//        Logininfo logininfo = logininfoMapper.loadByUsername(username,type);
//        if (logininfo==null){
//            throw  new BusinessException("账号不存在请注册");
//        }
//        /*判断密码是否和加盐加密的密码是否一致*/
//        String passwordTmp = MD5Utils.encrypByMd5(longin.getPassword() + logininfo.getSalt());
//        /*判断用户输入的密码和数据库的密码是否一致.如果不一致返回异常提示*/
//        if (!logininfo.getPassword().equals(passwordTmp)){
//            throw new BusinessException("密码输入错误,请重新输入");
//        }
//        /*设置返回的个参数token和logininfo*/
//        /*把uuid存入token中*/
//        String token = UUID.randomUUID().toString();
//        /*把两个参数存入redis中设置过期时间30分钟*/
//        redisTemplate.opsForValue().set(token,logininfo,30, TimeUnit.MINUTES);
//  //  5.将logininfo登录用户敏感信息设置为空
//        logininfo.setPassword("");
//        logininfo.setUsername("");
//        HashMap<String,Object> map = new HashMap<String,Object>();
//        map.put("token",token);
//        map.put("logininfo",logininfo);
//        return map;
//    }


    @Override
    public HashMap<String, Object> logininfo(Longin longin) {
        /*先获取从前端拿到的每个参数数据*/
        String password = longin.getPassword();
        String username = longin.getUsername();
        String type = longin.getType();
        /*给参数做非空判断*/
        if (StringUtils.isEmpty(password)
                ||StringUtils.isEmpty(username)
                ||StringUtils.isEmpty(type)){
            throw new BusinessException("参数不能为空,请重新输入");
        }
        /*根据账户和type查询登录用户是否存在不存在抛出异常*/
        /*因为是登录所以要查询登录里面用户是否存在,包含三个参数的是已经封装的对象Longin*/
        Logininfo logininfo = logininfoMapper.loadByUsername(username,type);
        if (logininfo==null){
            throw  new BusinessException("账号不存在请注册");
        }
        /*判断密码是否和加盐加密的密码是否一致*/
        String passwordTmp = MD5Utils.encrypByMd5(longin.getPassword() + logininfo.getSalt());
        /*判断用户输入的密码和数据库的密码是否一致.如果不一致返回异常提示*/
        if (!logininfo.getPassword().equals(passwordTmp)){
            throw new BusinessException("密码输入错误,请重新输入");
        }

        return  loginSuccessJwtHandler(logininfo);
    }
        // 因为有重复代码所以用抽取公共方法的思想来弄
    /**
     * @Title: loginSuccessJwtHandler
     * @Description: 用户登录成功后生成JWT加密串并放入到map对象中
     * @Author: Mr.She
     * @Version: 1.0
     * @Date:  2022/6/30 13:23
     * @Parameters: [cn.itsource.user.domain.Logininfo]
     * @Return java.util.Map<java.lang.String,java.lang.Object>
     */
    public HashMap<String,Object>loginSuccessJwtHandler(Logininfo logininfo){
        // 1.创建Userinfo对象,存储logininfo对象并用于后续赋值
        UserInfo userinfo = new UserInfo();
        userinfo.setLogininfo(logininfo);
        // 2.判断后台用户登录才需要设置菜单信息,权限信息,类型为0是后台1为用户
        if (logininfo.getType() == 0){
            // 获取权限用户的信息数量较多用集合接收
            List<String> permissions = permissionMapper.loadOwnPermissionByLogininId(logininfo.getId());
            // 获取用户菜单信息
            List<Menu> menus = menuMapper.loadOwnMenuByLogininId(logininfo.getId());
            // 将菜单信息,权限信息放入userinfo对象中
            userinfo.setPermissions(permissions);
            userinfo.setMenus(menus);
        }
        // 3.根据密钥文件名获取到密钥
        PrivateKey privateKey =

        // 3.根据密钥文件名称获取到密钥
       privateKey = RsaUtils.getPrivateKey(this.getClass().getClassLoader().getResource(jwtpri).getFile());
        // 4.根据密钥,用户信息,获取jwt字符串的token
        String tokn = JwtUtils.generateTokenExpireInMinutes(userinfo, privateKey, 60*24);
        // 5.按照正常逻辑我们直接返回token给前端,是二次加密前端需要解密所以我们使用简化版jwt
        // 封装token.用户信息 权限信息菜单信息给前端
        HashMap<String, Object> map = new HashMap<>();
        map.put("token", tokn);
        map.put("logininfo", logininfo);
        map.put("permissions", userinfo.getPermissions());
        map.put("menus", userinfo.getMenus());
        System.out.println("展示map数据"+map);
        return map;
    }



    @Override
    public AjaxResult wechat(Wecheatdto wecheatdto) {
        /*先获取前端传输过来的数值*/
        String code = wecheatdto.getCode();

        /*1.实现非空判断*/
        if (StringUtils.isEmpty(code)){
            throw new BusinessException("校验参数是否非空");
        }
        /* 2.拼接请求地址URL&APPID&SECRET的值*/
      String url =  BaseConstants.WecheatConstants.TOKEN_URL
              .replace("APPID",BaseConstants.WecheatConstants.APPID)
              .replace("SECRET",BaseConstants.WecheatConstants.SECRET)
              .replace("CODE",code);

        // 3.发送get请求获取到JSON字符串,该参数有appid和SECRET与CODE三个参数
        String jsonstr = HttpUtil.httpGet(url);
        JSONObject jsonObj = JSONObject.parseObject(jsonstr);
        // 获取access_token的值
        String access_token = jsonObj.getString("access_token");
        // 获取openid
        String openid = jsonObj.getString("openid");
        System.out.println(openid);
        // 5.根据openId查询t_wxuser openid是微信用戶的唯一表示
      Wxuser wxuser =  wxuserMapper.loadByopenId(openid);
        // 6.判断微信用户信息是否存在
        if (wxuser!=null&&wxuser.getUserId()!=null){
            // 如果存在就获取登录信息
            Long userId = wxuser.getUserId();
            Logininfo logininfo = logininfoMapper.loadByUserId(userId);
            System.out.println(logininfo);
            /*把uuid存入token中*/
            String token = UUID.randomUUID().toString();
            /*把两个参数存入redis中设置过期时间30分钟*/
            redisTemplate.opsForValue().set(token,logininfo,30, TimeUnit.MINUTES);
            //  5.将logininfo登录用户敏感信息设置为空
            logininfo.setPassword("");
            logininfo.setUsername("");
            /*把参数存入map中并且传入前端*/
            HashMap<String,Object> map = new HashMap<String,Object>();
            map.put("token",token);
            map.put("logininfo",logininfo);



            return  AjaxResult.me().setResultObj(loginSuccessJwtHandler(logininfo));
        }
        /*凭借stringbuffer*/
        String paramStr = "?accessToken=" + jsonObj.getString("access_token") + "&openId=" + jsonObj.getString("openid");;
        return AjaxResult.me().setSuccess(false).setMessage("nobinder").setResultObj(paramStr);
     }

    @Override
    public Map<String, Object> binder(Wecheatdto wecheatdto) {
        // 1.非空校验
        String verifyCode = wecheatdto.getVerifyCode();
        String phone = wecheatdto.getPhone();
        String accessToken = wecheatdto.getAccessToken();
        String openId = wecheatdto.getOpenId();
        System.out.println(phone);
        System.out.println(accessToken);
        System.out.println(openId);
        if (StringUtils.isEmpty(verifyCode)
                || StringUtils.isEmpty(phone)
                || StringUtils.isEmpty(accessToken)
                || StringUtils.isEmpty(openId)
                ){

            throw new BusinessException("参数异常！");
        }
       ;
        // 2.手机验证码校验
        // 1.为空校验

        String phoneCodeKey =  BaseConstants.VerifyConstants.BUSINESS_BINDER_KEY + phone;
        Object phoneCodeValueRedis = redisTemplate.opsForValue().get(phoneCodeKey);
        // 3.判断验证码是否正确
        String phoneCode = phoneCodeValueRedis.toString().split(":")[1];
        if (!phoneCode.equalsIgnoreCase(verifyCode)){
            throw new BusinessException("手机验证码错误，请重新输入！");
        }
        // 3.根据手机号查询用户是否存在 如果這個用戶之前注冊過,但是沒有使用微信登錄
        User user = userMapper.loadByPhone(phone);
        // 3.1.不存在：初始化user&logininfo并保存
        Logininfo logininfo = null;
        if (user == null){
            user = smsCodeDto2User(phone);
            logininfo = user2Logininfo(user);
            logininfoMapper.save(logininfo);
            user.setLogininfoId(logininfo.getId());
            userMapper.save(user);
        }else {// 3.2.存在：查询Logininfo
            logininfo = logininfoMapper.loadByUserId(user.getId());
        }
        // 4.根据前端传递的accessToken&openId查询微信用户基本信息
         String wxUserUrl = BaseConstants.WecheatConstants.WXUSER_URL
                .replace("ACCESS_TOKEN", accessToken)
                .replace("OPENID", openId);
        String wxUserStr = HttpUtil.httpGet(wxUserUrl);
        // 5.通过JSONObject方法将JSON字符串转换为指定对象
        Wxuser wxuser = JSONObject.parseObject(wxUserStr, Wxuser.class);
        // 5.保存wxUser对象，并且需要设置userId字段为user对象的主键
        wxuser.setUserId(user.getId());
        wxuserMapper.save(wxuser);
        // 6.返回成功登录map
        // 5.生成UUID作为key
        String token = UUID.randomUUID().toString();
        // 6.将uuid&&logininfo设置到redis中，设置30分钟有效期
        redisTemplate.opsForValue().set(token, logininfo, 30, TimeUnit.MINUTES);
        // 7.把Logininfo对象的敏感信息置为空，作为value
        logininfo.setSalt("");
        logininfo.setPassword("");
        // 8.将uuid&&logininfo存入到map中，返回给前端
        Map<String, Object> map = new HashMap<>();
        map.put("token", token);
        map.put("logininfo", logininfo);
        return loginSuccessJwtHandler(logininfo);
    }


    private User smsCodeDto2User(String phone) {
        User user = new User();
        user.setUsername(phone);
        user.setPhone(phone);
        String salt = StrUtils.getComplexRandomString(32);
        String saltPassword = MD5Utils.encrypByMd5(salt + "1");
        user.setPassword(saltPassword);
        user.setSalt(salt);
        // 此处最好给用户发个短信，告知他初始密码是多少
        return user;
    }

    private Logininfo user2Logininfo(User user) {
        Logininfo logininfo = new Logininfo();
        BeanUtils.copyProperties(user, logininfo);
        logininfo.setType(1);
        return logininfo;
    }
}
